package com.zipking.cloud.springbootmybatis.redislock2;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁切片
 * @author wangzhenjun
 * @date 2023/8/31 9:28
 */
@Slf4j
@Aspect
@RequiredArgsConstructor
@Component
public class RedisLockAspect {
@Autowired
    private   RedissonClient redissonClient;
    private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
    private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();


    /**
     * 环绕切片
     */
    @Around("@annotation(redisLock)")
    public Object aroundRedisLock(ProceedingJoinPoint point, RedisLock redisLock) throws Throwable {
        log.info("=====请求来排队尝试获取锁=====");
        String value = redisLock.value();
        Assert.hasText(value, "@RedisLock key不能为空！");
        boolean el = redisLock.isEl();
        String lockKey;
        if (el) {
            lockKey = evaluateExpression(value, point);
        } else {
            lockKey = value;
        }
        log.info("========解析后的lockKey :{}", lockKey);
        long waitTime = redisLock.waitTime();
        long leaseTime = redisLock.leaseTime();
        TimeUnit timeUnit = redisLock.timeUnit();

        RLock lock = redissonClient.getLock(lockKey);
//        lock.tryLock(waitTime, leaseTime, timeUnit);
//        lock.lock(leaseTime, timeUnit);
//        lock.lock();
        boolean tryLock = lock.tryLock(waitTime, leaseTime, timeUnit);
        log.info("tryLock:{}",tryLock);
        if (!tryLock) {
            System.out.println("锁被占用，请稍后提交！");
            return null;
           // throw new ServiceException("锁被占用，请稍后提交！");
        }
        try {
            return point.proceed();
        } catch (Throwable throwable) {
            log.error("方法执行失败：", throwable.getMessage());
            throw throwable;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 解析el表达式
     * @param expression
     * @param point
     * @return
     */
    private String evaluateExpression(String expression, ProceedingJoinPoint point) {
        // 获取目标对象
        Object target = point.getTarget();
        // 获取方法参数
        Object[] args = point.getArgs();
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();

        EvaluationContext context = new MethodBasedEvaluationContext(target, method, args, parameterNameDiscoverer);
        Expression exp = spelExpressionParser.parseExpression(expression);
        return exp.getValue(context, String.class);
    }
}